/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: Dec 31, 2009
 * Time: 10:48:36 AM
 * To change this template use File | Settings | File Templates.
 */
import dk.brics.automaton.Automaton;
import dk.brics.automaton.RunAutomaton;
import java.util.*;
import org.jdom.*;

// Referenced classes of package dk.brics.dsd:
//            InternalSchemaErrorException, SequenceRegexp, OptionalRegexp, ComplementRegexp,
//            UnionRegexp, IntersectionRegexp, MinusRegexp, RepeatRegexp,
//            StringRegexp, CharRegexp, ReferenceRegexp, BoolexpRegexp,
//            Context, Schema, Boolexp, Validator

abstract class Regexp
{

    Regexp()
    {
    }

    abstract Element toXML(Context context);

    static List parseList(Element element, Schema schema)
    {
        ArrayList arraylist = new ArrayList();
        Iterator iterator = element.getChildren().iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            Element element1 = (Element)iterator.next();
            if(Schema.isDSDElement(element1))
                arraylist.add(parse(element1, schema));
        } while(true);
        return arraylist;
    }

    static Regexp parseOne(Element element, Schema schema)
    {
        Regexp regexp = parseOneOptional(element, schema);
        if(regexp == null)
            throw new InternalSchemaErrorException();
        else
            return regexp;
    }

    static Regexp parseOneOptional(Element element, Schema schema)
    {
        for(Iterator iterator = element.getChildren().iterator(); iterator.hasNext();)
        {
            Element element1 = (Element)iterator.next();
            if(Schema.isDSDElement(element1))
                return parse(element1, schema);
        }

        return null;
    }

    static Regexp parse(Element element, Schema schema)
    {
        String s = element.getName();
        Object obj;
        if(s.equals("sequence"))
            obj = new SequenceRegexp(element, schema);
        else
        if(s.equals("optional"))
            obj = new OptionalRegexp(element, schema);
        else
        if(s.equals("complement"))
            obj = new ComplementRegexp(element, schema);
        else
        if(s.equals("union"))
            obj = new UnionRegexp(element, schema);
        else
        if(s.equals("intersection"))
            obj = new IntersectionRegexp(element, schema);
        else
        if(s.equals("minus"))
            obj = new MinusRegexp(element, schema);
        else
        if(s.equals("repeat"))
            obj = new RepeatRegexp(element, schema);
        else
        if(s.equals("string"))
            obj = new StringRegexp(element, schema);
        else
        if(s.equals("char"))
            obj = new CharRegexp(element, schema);
        else
        if(s.equals("stringtype") || s.equals("contenttype"))
            obj = new ReferenceRegexp(element, schema);
        else
            obj = new BoolexpRegexp(element, schema);
        return ((Regexp) (obj));
    }

    boolean isRepeatUnion(Context context)
    {
        if(is_repeatunion == null)
            is_repeatunion = Boolean.valueOf(checkRepeatUnion(context));
        return is_repeatunion.booleanValue();
    }

    boolean checkRepeatUnion(Context context)
    {
        return false;
    }

    boolean checkUnion(Context context)
    {
        return false;
    }

    boolean checkSimple(Context context)
    {
        return false;
    }

    Automaton getAutomaton(Context context)
    {
        if(automaton == null)
            automaton = toAutomaton(context);
        return automaton;
    }

    RunAutomaton getRunAutomaton(Context context)
    {
        if(run == null)
            run = new RunAutomaton(getAutomaton(context));
        return run;
    }

    Set getBoolexps(Context context)
    {
        if(boolexps == null)
        {
            boolexps = new HashSet();
            addBoolexps(context, boolexps);
        }
        return boolexps;
    }

    abstract void getMentioned(Context context, Set set, List list);

    void addBoolexps(Context context, Set set)
    {
    }

    private static boolean anyGaps(Context context)
    {
        for(Iterator iterator = context.e.getContent().iterator(); iterator.hasNext();)
        {
            Object obj = iterator.next();
            if((obj instanceof Element) && Template.isGap((Element)obj) && context.sg != null)
                return true;
        }

        return false;
    }

    private String buildContentsList(Context context, boolean flag, boolean flag1, Set set, Map map)
    {
        StringBuffer stringbuffer = new StringBuffer();
        Iterator iterator = context.e.getContent().iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            Object obj = iterator.next();
            if(set.contains(null))
            {
                String s = null;
                if(obj instanceof Text)
                    s = ((Text)obj).getText();
                else
                if(obj instanceof CDATA)
                    s = ((CDATA)obj).getText();
                if(s != null)
                {
                    if(flag)
                        s = s.toUpperCase();
                    else
                    if(flag1)
                        s = s.toLowerCase();
                    stringbuffer.append(s);
                }
            }
            if(obj instanceof Element)
            {
                char c = addSymbol(context, (Element)obj, set, null, map);
                if(c != 0)
                    stringbuffer.append(c);
            }
        } while(true);
        return stringbuffer.toString();
    }

    private char addSymbol(Context context, Element element, Set set, List list, Map map)
    {
        char c = '\0';
        String s = element.getNamespaceURI();
        if(!s.equals("http://www.brics.dk/DSD/2.0/error") && !s.equals("http://www.brics.dk/DSD/2.0/meta") && set.contains(element))
        {
            if(next_cp == '\uF900')
                throw new RuntimeException("too long contents sequence");
            c = next_cp++;
            if(list != null)
                list.add(Automaton.makeChar(c));
            Iterator iterator = boolexps.iterator();
            do
            {
                if(!iterator.hasNext())
                    break;
                BoolexpRegexp boolexpregexp = (BoolexpRegexp)iterator.next();
                if(boolexpregexp.exp.evaluateMemo(new Context(element, context.this_e, context.v)) == 1)
                {
                    Character character = new Character(boolexpregexp.cp);
                    Set set1 = (Set)map.get(character);
                    set1.add(new Character(c));
                }
            } while(true);
        }
        return c;
    }

    private void getContentElements(Context context, List list, List list1, Set set)
    {
        if(set.contains(context))
            return;
        set.add(context);
        Iterator iterator = list.iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            Object obj = iterator.next();
            if(obj instanceof Element)
            {
                Element element = (Element)obj;
                if(Template.isGap(element) && context.sg != null)
                {
                    String s = Template.getGap(element);
                    Iterator iterator1 = context.n.lookupTemplateEdges(s).iterator();
                    while(iterator1.hasNext())
                    {
                        Node node = ((TemplateEdge)iterator1.next()).getDestination();
                        getContentElements(new Context(context.sg, node, null, context.this_n, context.this_e, context.v), node.getTemplate().getXML(), list1, set);
                    }
                } else
                {
                    String s1 = element.getNamespaceURI();
                    if(!s1.equals("http://www.brics.dk/DSD/2.0/error") && !s1.equals("http://www.brics.dk/DSD/2.0/meta"))
                        list1.add(element);
                }
            }
        } while(true);
    }

    private void buildApproximatingContentsAutomaton(Context context, List list, List list1, Set set, Set set1, Map map)
    {
        if(set1.contains(context))
            return;
        set1.add(context);
        Iterator iterator = list.iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            Object obj = iterator.next();
            if(set.contains(null))
            {
                String s = null;
                if(obj instanceof Text)
                    s = ((Text)obj).getText();
                else
                if(obj instanceof CDATA)
                    s = ((CDATA)obj).getText();
                if(s != null)
                    list1.add(Automaton.makeString(s));
            }
            if(obj instanceof Element)
            {
                Element element = (Element)obj;
                if(Template.isGap(element) && context.sg != null)
                {
                    String s1 = Template.getGap(element);
                    Iterator iterator1 = context.n.lookupTemplateEdges(s1).iterator();
                    while(iterator1.hasNext())
                    {
                        Node node = ((TemplateEdge)iterator1.next()).getDestination();
                        buildApproximatingContentsAutomaton(new Context(context.sg, node, null, context.this_n, context.this_e, context.v), node.getTemplate().getXML(), list1, set, set1, map);
                    }
                } else
                {
                    addSymbol(context, element, set, list1, map);
                }
            }
        } while(true);
    }

    private Automaton buildContentsAutomaton(Context context, List list, Set set, Stack stack, Map map)
    {
        if(stack.contains(context))
        {
            approximating = true;
            ArrayList arraylist = new ArrayList();
            buildApproximatingContentsAutomaton(context, list, arraylist, set, new HashSet(), map);
            Automaton automaton1 = Automaton.union(arraylist);
            automaton1.minimize();
            automaton1 = automaton1.repeat();
            automaton1.minimize();
            return automaton1;
        }
        stack.push(context);
        ArrayList arraylist1 = new ArrayList();
        Iterator iterator = list.iterator();
        do
        {
            if(!iterator.hasNext())
                break;
            Automaton automaton2 = null;
            Object obj = iterator.next();
            if(set.contains(null))
            {
                String s = null;
                if(obj instanceof Text)
                    s = ((Text)obj).getText();
                else
                if(obj instanceof CDATA)
                    s = ((CDATA)obj).getText();
                if(s != null)
                    automaton2 = Automaton.makeString(s);
            }
            if(obj instanceof Element)
            {
                Element element = (Element)obj;
                ArrayList arraylist2 = new ArrayList();
                if(Template.isGap(element) && context.sg != null)
                {
                    String s1 = Template.getGap(element);
                    if(context.sg.getOpenTemplateGaps().contains(s1))
                        arraylist2.add(Automaton.makeEmptyString());
                    Node node;
                    for(Iterator iterator1 = context.n.lookupTemplateEdges(s1).iterator(); iterator1.hasNext(); arraylist2.add(buildContentsAutomaton(new Context(context.sg, node, null, context.this_n, context.this_e, context.v), node.getTemplate().getXML(), set, stack, map)))
                        node = ((TemplateEdge)iterator1.next()).getDestination();

                } else
                {
                    addSymbol(context, element, set, arraylist2, map);
                }
                if(arraylist2.size() > 0)
                {
                    automaton2 = Automaton.union(arraylist2);
                    automaton2.minimize();
                }
            }
            if(automaton2 != null)
                arraylist1.add(automaton2);
        } while(true);
        Automaton automaton3 = Automaton.concatenate(arraylist1);
        automaton3.minimize();
        stack.pop();
        return automaton3;
    }

    byte matchContents(Context context, Set set)
    {
        boolexps = getBoolexps(context);
        HashSet hashset = new HashSet();
        ArrayList arraylist = new ArrayList();
        getContentElements(context, context.e.getContent(), arraylist, new HashSet());
        getMentioned(context, hashset, arraylist);
        if(set != null)
            set.addAll(hashset);
        String s = null;
        String s1 = null;
        if(context.sg != null)
        {
            s = (String)context.v.contents_whitespace.get(context.e);
            s1 = (String)context.v.contents_case.get(context.e);
        }
        Automaton automaton1 = getAutomaton(context);
        automaton1 = normalizeWhitespace(automaton1, s);
        next_cp = '\uE000';
        HashMap hashmap = new HashMap();
        BoolexpRegexp boolexpregexp;
        for(Iterator iterator = boolexps.iterator(); iterator.hasNext(); hashmap.put(new Character(boolexpregexp.cp), new HashSet()))
            boolexpregexp = (BoolexpRegexp)iterator.next();

        if(context.sg == null || !anyGaps(context))
        {
            String s2 = buildContentsList(context, s1 != null && s1.equals("upper"), s1 != null && s1.equals("lower"), hashset, hashmap);
            automaton1 = automaton1.subst(hashmap);
            return (byte) (!automaton1.run(s2) ? 2 : 1);
        }
        approximating = false;
        Automaton automaton2;
        if(isRepeatUnion(context))
        {
            ArrayList arraylist1 = new ArrayList();
            buildApproximatingContentsAutomaton(context, context.e.getContent(), arraylist1, hashset, new HashSet(), hashmap);
            automaton2 = Automaton.union(arraylist1);
            automaton2.minimize();
            automaton2 = automaton2.repeat();
            automaton2.minimize();
        } else
        {
            automaton2 = buildContentsAutomaton(context, context.e.getContent(), hashset, new Stack(), hashmap);
        }
        automaton2 = normalizeCase(automaton2, s1);
        automaton1 = automaton1.subst(hashmap);
        if(automaton2.subsetOf(automaton1))
            return 1;
        Automaton automaton3 = automaton2.intersection(automaton1);
        if(automaton3.isEmpty())
            return 2;
        return (byte) (!approximating ? 3 : 4);
    }

    boolean match(String s, Context context)
    {
        return getRunAutomaton(context).run(s);
    }

    abstract Automaton toAutomaton(Context context);

    boolean isChar(Context context)
    {
        return false;
    }

    static Automaton getAllchars()
    {
        if(allchars == null)
        {
            Automaton automaton1 = Automaton.makeCharRange('\uF900', '\uFFFF');
            allchars = Automaton.makeCharRange('\0', '\uDFFF').union(automaton1);
            allchars.minimize();
        }
        return allchars;
    }

    static Automaton makeCharRanges(char ac[])
    {
        ArrayList arraylist = new ArrayList();
        for(int i = 0; i < ac.length; i += 2)
            arraylist.add(Automaton.makeCharRange(ac[i], ac[i + 1]));

        return Automaton.union(arraylist);
    }

    static Automaton getExtender()
    {
        if(extender == null)
        {
            Validator.debug("building EXTENDER automaton");
            char ac[] = {
                '\u3031', '\u3035', '\u309D', '\u309E', '\u30FC', '\u30FE'
            };
            String s = "\267\u02D0\u02D1\u0387\u0640\u0E46\u0EC6\u3005";
            extender = makeCharRanges(ac).union(Automaton.makeCharSet(s));
            Validator.debug("done building EXTENDER automaton");
        }
        return extender;
    }

    static Automaton getCombiningchar()
    {
        if(combiningchar == null)
        {
            Validator.debug("building COMBININGCHAR automaton");
            char ac[] = {
                '\u0300', '\u0345', '\u0360', '\u0361', '\u0483', '\u0486', '\u0591', '\u05A1', '\u05A3', '\u05B9',
                '\u05BB', '\u05BD', '\u05C1', '\u05C2', '\u064B', '\u0652', '\u06D6', '\u06DC', '\u06DD', '\u06DF',
                '\u06E0', '\u06E4', '\u06E7', '\u06E8', '\u06EA', '\u06ED', '\u0901', '\u0903', '\u093E', '\u094C',
                '\u0951', '\u0954', '\u0962', '\u0963', '\u0981', '\u0983', '\u09C0', '\u09C4', '\u09C7', '\u09C8',
                '\u09CB', '\u09CD', '\u09E2', '\u09E3', '\u0A40', '\u0A42', '\u0A47', '\u0A48', '\u0A4B', '\u0A4D',
                '\u0A70', '\u0A71', '\u0A81', '\u0A83', '\u0ABE', '\u0AC5', '\u0AC7', '\u0AC9', '\u0ACB', '\u0ACD',
                '\u0B01', '\u0B03', '\u0B3E', '\u0B43', '\u0B47', '\u0B48', '\u0B4B', '\u0B4D', '\u0B56', '\u0B57',
                '\u0B82', '\u0B83', '\u0BBE', '\u0BC2', '\u0BC6', '\u0BC8', '\u0BCA', '\u0BCD', '\u0C01', '\u0C03',
                '\u0C3E', '\u0C44', '\u0C46', '\u0C48', '\u0C4A', '\u0C4D', '\u0C55', '\u0C56', '\u0C82', '\u0C83',
                '\u0CBE', '\u0CC4', '\u0CC6', '\u0CC8', '\u0CCA', '\u0CCD', '\u0CD5', '\u0CD6', '\u0D02', '\u0D03',
                '\u0D3E', '\u0D43', '\u0D46', '\u0D48', '\u0D4A', '\u0D4D', '\u0E34', '\u0E3A', '\u0E47', '\u0E4E',
                '\u0EB4', '\u0EB9', '\u0EBB', '\u0EBC', '\u0EC8', '\u0ECD', '\u0F18', '\u0F19', '\u0F71', '\u0F84',
                '\u0F86', '\u0F8B', '\u0F90', '\u0F95', '\u0F99', '\u0FAD', '\u0FB1', '\u0FB7', '\u20D0', '\u20DC',
                '\u302A', '\u302F'
            };
            String s = "\u05BF\u05C4\u0670\u093C\u094D\u09BC\u09BE\u09BF\u09D7\u0A02\u0A3C\u0A3E\u0A3F\u0ABC\u0B3C\u0BD7\u0D57\u0E31\u0EB1\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F97\u0FB9\u20E1\u3099\u309A";
            combiningchar = makeCharRanges(ac).union(Automaton.makeCharSet(s));
            combiningchar.minimize();
            Validator.debug("done building COMBININGCHAR automaton");
        }
        return combiningchar;
    }

    static Automaton getDigit()
    {
        if(digit == null)
        {
            Validator.debug("building DIGIT automaton");
            char ac[] = {
                '0', '9', '\u0660', '\u0669', '\u06F0', '\u06F9', '\u0966', '\u096F', '\u09E6', '\u09EF',
                '\u0A66', '\u0A6F', '\u0AE6', '\u0AEF', '\u0B66', '\u0B6F', '\u0BE7', '\u0BEF', '\u0C66', '\u0C6F',
                '\u0CE6', '\u0CEF', '\u0D66', '\u0D6F', '\u0E50', '\u0E59', '\u0ED0', '\u0ED9', '\u0F20', '\u0F29'
            };
            digit = makeCharRanges(ac);
            Validator.debug("done building DIGIT automaton");
        }
        return digit;
    }

    static Automaton getIdeographic()
    {
        if(ideographic == null)
        {
            Validator.debug("building IDEOGRAPHIC automaton");
            char ac[] = {
                '\u4E00', '\u9FA5', '\u3021', '\u3029'
            };
            String s = "\u3007";
            ideographic = makeCharRanges(ac).union(Automaton.makeCharSet(s));
            ideographic.minimize();
            Validator.debug("done building IDEOGRAPHIC automaton");
        }
        return ideographic;
    }

    static Automaton getBasechar()
    {
        if(basechar == null)
        {
            Validator.debug("building BASECHAR automaton");
            char ac[] = {
                'A', 'Z', 'a', 'z', '\300', '\326', '\330', '\366', '\370', '\377',
                '\u0100', '\u0131', '\u0134', '\u013E', '\u0141', '\u0148', '\u014A', '\u017E', '\u0180', '\u01C3',
                '\u01CD', '\u01F0', '\u01F4', '\u01F5', '\u01FA', '\u0217', '\u0250', '\u02A8', '\u02BB', '\u02C1',
                '\u0388', '\u038A', '\u038E', '\u03A1', '\u03A3', '\u03CE', '\u03D0', '\u03D6', '\u03E2', '\u03F3',
                '\u0401', '\u040C', '\u040E', '\u044F', '\u0451', '\u045C', '\u045E', '\u0481', '\u0490', '\u04C4',
                '\u04C7', '\u04C8', '\u04CB', '\u04CC', '\u04D0', '\u04EB', '\u04EE', '\u04F5', '\u04F8', '\u04F9',
                '\u0531', '\u0556', '\u0561', '\u0586', '\u05D0', '\u05EA', '\u05F0', '\u05F2', '\u0621', '\u063A',
                '\u0641', '\u064A', '\u0671', '\u06B7', '\u06BA', '\u06BE', '\u06C0', '\u06CE', '\u06D0', '\u06D3',
                '\u06E5', '\u06E6', '\u0905', '\u0939', '\u0958', '\u0961', '\u0985', '\u098C', '\u098F', '\u0990',
                '\u0993', '\u09A8', '\u09AA', '\u09B0', '\u09B6', '\u09B9', '\u09DC', '\u09DD', '\u09DF', '\u09E1',
                '\u09F0', '\u09F1', '\u0A05', '\u0A0A', '\u0A0F', '\u0A10', '\u0A13', '\u0A28', '\u0A2A', '\u0A30',
                '\u0A32', '\u0A33', '\u0A35', '\u0A36', '\u0A38', '\u0A39', '\u0A59', '\u0A5C', '\u0A72', '\u0A74',
                '\u0A85', '\u0A8B', '\u0A8F', '\u0A91', '\u0A93', '\u0AA8', '\u0AAA', '\u0AB0', '\u0AB2', '\u0AB3',
                '\u0AB5', '\u0AB9', '\u0B05', '\u0B0C', '\u0B0F', '\u0B10', '\u0B13', '\u0B28', '\u0B2A', '\u0B30',
                '\u0B32', '\u0B33', '\u0B36', '\u0B39', '\u0B5C', '\u0B5D', '\u0B5F', '\u0B61', '\u0B85', '\u0B8A',
                '\u0B8E', '\u0B90', '\u0B92', '\u0B95', '\u0B99', '\u0B9A', '\u0B9E', '\u0B9F', '\u0BA3', '\u0BA4',
                '\u0BA8', '\u0BAA', '\u0BAE', '\u0BB5', '\u0BB7', '\u0BB9', '\u0C05', '\u0C0C', '\u0C0E', '\u0C10',
                '\u0C12', '\u0C28', '\u0C2A', '\u0C33', '\u0C35', '\u0C39', '\u0C60', '\u0C61', '\u0C85', '\u0C8C',
                '\u0C8E', '\u0C90', '\u0C92', '\u0CA8', '\u0CAA', '\u0CB3', '\u0CB5', '\u0CB9', '\u0CE0', '\u0CE1',
                '\u0D05', '\u0D0C', '\u0D0E', '\u0D10', '\u0D12', '\u0D28', '\u0D2A', '\u0D39', '\u0D60', '\u0D61',
                '\u0E01', '\u0E2E', '\u0E32', '\u0E33', '\u0E40', '\u0E45', '\u0E81', '\u0E82', '\u0E87', '\u0E88',
                '\u0E94', '\u0E97', '\u0E99', '\u0E9F', '\u0EA1', '\u0EA3', '\u0EAA', '\u0EAB', '\u0EAD', '\u0EAE',
                '\u0EB2', '\u0EB3', '\u0EC0', '\u0EC4', '\u0F40', '\u0F47', '\u0F49', '\u0F69', '\u10A0', '\u10C5',
                '\u10D0', '\u10F6', '\u1102', '\u1103', '\u1105', '\u1107', '\u110B', '\u110C', '\u110E', '\u1112',
                '\u1154', '\u1155', '\u115F', '\u1161', '\u116D', '\u116E', '\u1172', '\u1173', '\u11AE', '\u11AF',
                '\u11B7', '\u11B8', '\u11BC', '\u11C2', '\u1E00', '\u1E9B', '\u1EA0', '\u1EF9', '\u1F00', '\u1F15',
                '\u1F18', '\u1F1D', '\u1F20', '\u1F45', '\u1F48', '\u1F4D', '\u1F50', '\u1F57', '\u1F5F', '\u1F7D',
                '\u1F80', '\u1FB4', '\u1FB6', '\u1FBC', '\u1FC2', '\u1FC4', '\u1FC6', '\u1FCC', '\u1FD0', '\u1FD3',
                '\u1FD6', '\u1FDB', '\u1FE0', '\u1FEC', '\u1FF2', '\u1FF4', '\u1FF6', '\u1FFC', '\u212A', '\u212B',
                '\u2180', '\u2182', '\u3041', '\u3094', '\u30A1', '\u30FA', '\u3105', '\u312C', '\uAC00', '\uD7A3'
            };
            String s = "\u0386\u038C\u03DA\u03DC\u03DE\u03E0\u0559\u06D5\u093D\u09B2\u0A5E\u0A8D\u0ABD\u0AE0\u0B3D\u0B9C\u0CDE\u0E30\u0E84\u0E8A\u0E8D\u0EA5\u0EA7\u0EB0\u0EBD\u1100\u1109\u113C\u113E\u1140\u114C\u114E\u1150\u1159\u1163\u1165\u1167\u1169\u1175\u119E\u11A8\u11AB\u11BA\u11EB\u11F0\u11F9\u1F59\u1F5B\u1F5D\u1FBE\u2126\u212E";
            basechar = makeCharRanges(ac).union(Automaton.makeCharSet(s));
            basechar.minimize();
            Validator.debug("done building BASECHAR automaton");
        }
        return basechar;
    }

    static Automaton getLetter()
    {
        if(letter == null)
        {
            Validator.debug("building LETTER automaton");
            letter = getBasechar().union(getIdeographic());
            letter.minimize();
            Validator.debug("done building LETTER automaton");
        }
        return letter;
    }

    static Automaton getNcnamechar()
    {
        if(ncnamechar == null)
        {
            Validator.debug("building NCNAMECHAR automaton");
            ArrayList arraylist = new ArrayList();
            arraylist.add(getLetter());
            arraylist.add(getDigit());
            arraylist.add(Automaton.makeCharSet(".-_"));
            arraylist.add(getCombiningchar());
            arraylist.add(getExtender());
            ncnamechar = Automaton.union(arraylist);
            ncnamechar.minimize();
            Validator.debug("done building NCNAMECHAR automaton");
        }
        return ncnamechar;
    }

    static Automaton getNamechar()
    {
        if(namechar == null)
        {
            Validator.debug("building NAMECHAR automaton");
            namechar = getNcnamechar().union(Automaton.makeCharSet(":"));
            namechar.minimize();
            Validator.debug("done building NAMECHAR automaton");
        }
        return namechar;
    }

    static Automaton getNcname()
    {
        if(ncname == null)
        {
            Validator.debug("building NCNAME automaton");
            Automaton automaton1 = getLetter().union(Automaton.makeCharSet("_"));
            Automaton automaton2 = getNamechar().repeat();
            ncname = automaton1.concatenate(automaton2);
            ncname.minimize();
            Validator.debug("done building NCNAME automaton");
        }
        return ncname;
    }

    static Automaton getQname()
    {
        if(qname == null)
        {
            Validator.debug("building QNAME automaton");
            Automaton automaton1 = getNcname().concatenate(Automaton.makeCharSet(":")).optional();
            qname = automaton1.concatenate(getNcname());
            Validator.debug("done building QNAME automaton");
        }
        return qname;
    }

    static Automaton getPname()
    {
        if(pname == null)
        {
            Validator.debug("building PNAME automaton");
            Automaton automaton1 = getNcname().concatenate(Automaton.makeCharSet(":"));
            pname = automaton1.union(getQname());
            pname.minimize();
            Validator.debug("done building PNAME automaton");
        }
        return pname;
    }

    static boolean isPname(String s)
    {
        if(pname_run == null)
            pname_run = new RunAutomaton(getPname());
        return pname_run.run(s);
    }

    static Automaton normalizeWhitespace(Automaton automaton1, String s)
    {
        if(s == null || s.equals("preserve"))
            return automaton1;
        if(s.equals("compress"))
            automaton1 = automaton1.compress(" \t\r\n", ' ');
        else
        if(s.equals("trim"))
            automaton1 = automaton1.trim(" \t\r\n", ' ');
        automaton1.minimize();
        return automaton1;
    }

    static Automaton normalizeCase(Automaton automaton1, String s)
    {
        if(s == null || s.equals("preserve"))
            return automaton1;
        setIntervals();
        if(s.equals("upper"))
            automaton1 = automaton1.homomorph(source_upper, dest_upper);
        else
        if(s.equals("lower"))
            automaton1 = automaton1.homomorph(source_lower, dest_lower);
        return automaton1;
    }

    private static void setIntervals()
    {
        if(source_upper == null)
        {
            char ac[] = new char[0x10000];
            char ac1[] = new char[0x10000];
            int i = 0;
            char ac2[] = new char[0x10000];
            char ac3[] = new char[0x10000];
            int j = 0;
            for(int k = 0; k <= 65535; k++)
            {
                char c = Character.toUpperCase((char)k);
                if(i == 0 || k - ac[i - 1] != c - ac1[i - 1])
                {
                    ac[i] = (char)k;
                    ac1[i] = c;
                    i++;
                }
                char c1 = Character.toLowerCase((char)k);
                if(j == 0 || k - ac2[j - 1] != c1 - ac3[j - 1])
                {
                    ac2[j] = (char)k;
                    ac3[j] = c1;
                    j++;
                }
            }

            source_upper = new char[i];
            dest_upper = new char[i];
            for(int l = 0; l < i; l++)
            {
                source_upper[l] = ac[l];
                dest_upper[l] = ac1[l];
            }

            source_lower = new char[j];
            dest_lower = new char[j];
            for(int i1 = 0; i1 < j; i1++)
            {
                source_lower[i1] = ac2[i1];
                dest_lower[i1] = ac3[i1];
            }

        }
    }

    Automaton automaton;
    RunAutomaton run;
    Set boolexps;
    Boolean is_repeatunion;
    static Automaton allchars;
    static Automaton extender;
    static Automaton combiningchar;
    static Automaton digit;
    static Automaton ncnamechar;
    static Automaton ideographic;
    static Automaton basechar;
    static Automaton namechar;
    static Automaton letter;
    static Automaton ncname;
    static Automaton qname;
    static Automaton pname;
    static RunAutomaton pname_run;
    static char source_upper[];
    static char dest_upper[];
    static char source_lower[];
    static char dest_lower[];
    private boolean approximating;
    private char next_cp;
}
